
package com.gitee.flow;

import com.crenjoy.proto.mapper.BeanMapper;
import com.crenjoy.proto.mapper.ProtoMapper;
import com.crenjoy.proto.utils.AnyUtil;
import com.crenjoy.proto.utils.TypeUtil;
import com.gitee.flow.util.AnyUtilExtend;
import com.gitee.jmash.core.orm.DtoPageImpl;
import com.gitee.jmash.core.orm.DtoTotal;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.StringValue;
import java.math.BigDecimal;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import jmash.flow.protobuf.AssigneeList;
import jmash.flow.protobuf.DelegationStatus;
import jmash.flow.protobuf.DeployRes;
import jmash.flow.protobuf.HiCommentModel;
import jmash.flow.protobuf.HiProcInstModel;
import jmash.flow.protobuf.HiProcInstPage;
import jmash.flow.protobuf.Priority;
import jmash.flow.protobuf.ProcDefModel;
import jmash.flow.protobuf.ProcInstModel;
import jmash.flow.protobuf.ProcInstPage;
import jmash.flow.protobuf.TaskModel;
import jmash.flow.protobuf.TaskPage;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.CommentEntity;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

/**
 * Flow Mapper.
 *
 * @author <a href="mailto:service@crenjoy.com">crenjoy</a>
 *
 */
@Mapper
public interface FlowMapper extends BeanMapper, ProtoMapper {

  FlowMapper INSTANCE = Mappers.getMapper(FlowMapper.class);

  DeployRes deployment(Deployment entity);

  default List<ProcDefModel> listProcDef(List<ProcessDefinition> list) {
    Map<String, ProcDefModel.Builder> map = new HashMap<>();
    for (ProcessDefinition pd : list) {
      ProcDefModel model = modelProcDef(pd);
      String key = model.getTenantId() + ":" + model.getKey();
      if (map.containsKey(key)) {
        map.get(key).addChildren(model);
      } else {
        map.put(key, model.toBuilder());
      }
    }
    List<ProcDefModel> models = new ArrayList<>();
    for (ProcDefModel.Builder builder : map.values()) {
      models.add(builder.build());
    }
    return models;
  }

  ProcDefModel modelProcDef(ProcessDefinition entity);

  ProcInstPage pageProcInst(DtoPageImpl<ProcessInstance, DtoTotal> page);

  ProcInstModel modelProcInst(ProcessInstance entity);

  HiProcInstPage pageHiProcInst(DtoPageImpl<HistoricProcessInstance, DtoTotal> page);

  HiProcInstModel modelHiProcInst(HistoricProcessInstance entity);

  TaskPage pageTask(DtoPageImpl<Task, DtoTotal> page);

  TaskModel modelTask(Task task);

  /** 获取任务所属的实例更多信息. */
  default TaskModel modelTask(Task task, ProcessInstance procInst) {
    TaskModel model = FlowMapper.INSTANCE.modelTask(task);
    TaskModel.Builder builder = model.toBuilder().setProcInstName(procInst.getName());
    builder.setStartTime(TypeUtil.INSTANCE.toProtoTimestamp(procInst.getStartTime()));
    builder.setStartUserId(nullToProto(procInst.getStartUserId()));
    builder.setBusinessKey(nullToProto(procInst.getBusinessKey()));
    builder.setBusinessStatus(nullToProto(procInst.getBusinessStatus()));
    builder.setProcessDefinitionKey(nullToProto(procInst.getProcessDefinitionKey()));
    return builder.build();
  }

  List<HiCommentModel> listComment(List<CommentEntity> comments);

  HiCommentModel modelComment(CommentEntity comment);

  Map<String, Object> toMapObject(Map<String, Any> map);

  Map<String, Any> toMapAny(Map<String, Object> map);

  /** Any封包. */
  @SuppressWarnings("rawtypes")
  default <T> Any pack(Object obj) {
    if (obj instanceof Any) {
      return (Any) obj;
    } else if (obj instanceof com.google.protobuf.Message) {
      return AnyUtil.pack((com.google.protobuf.Message) obj);
    } else if (obj instanceof String) {
      return AnyUtil.pack((String) obj);
    } else if (obj instanceof Integer) {
      return AnyUtil.pack((Integer) obj);
    } else if (obj instanceof Long) {
      return AnyUtil.pack((Long) obj);
    } else if (obj instanceof Boolean) {
      return AnyUtil.pack((Boolean) obj);
    } else if (obj instanceof Float) {
      return AnyUtil.pack((Float) obj);
    } else if (obj instanceof Double) {
      return AnyUtil.pack((Double) obj);
    } else if (obj instanceof ByteString) {
      return AnyUtil.pack((ByteString) obj);
    } else if (obj instanceof byte[]) {
      return AnyUtil.pack((byte[]) obj);
    } else if (obj instanceof TemporalAccessor) {
      return AnyUtil.pack((TemporalAccessor) obj);
    } else if (obj instanceof BigDecimal) {
      return AnyUtil.pack((BigDecimal) obj);
    } else if (obj instanceof UUID) {
      return AnyUtil.pack((UUID) obj);
    } else if (obj instanceof Enum) {
      return AnyUtil.pack((Enum) obj);
    } else if (obj instanceof List<?>) {
      return AnyUtilExtend.pack((List<String>) obj);
    }
    return null;
  }

  /** Any拆包. */
  default Object unpack(Any obj) {
    return AnyUtil.unpack(obj);
  }

  /** 委托状态枚举转换. */
  default DelegationStatus toDelegationStatus(DelegationState state) {
    if (state == null) {
      return DelegationStatus.NONE;
    }
    return DelegationStatus.valueOf(state.name());
  }

  /** 委托状态枚举转换. */
  default DelegationState toDelegationState(DelegationStatus status) {
    if (status == null || DelegationStatus.NONE.equals(status)) {
      return null;
    }
    return DelegationState.valueOf(status.name());
  }

  /** 优先级转换. */
  default Priority toPriority(int priority) {
    if (priority <= 0) {
      // 默认
      return Priority.Trivial;
    } else if (priority > 0 && priority <= 50) {
      // 次重要
      return Priority.Minor;
    } else if (priority > 50 && priority <= 100) {
      // 重要
      return Priority.Major;
    } else {
      // 关键
      return Priority.Critical;
    }
  }

  default AssigneeList list(List<String> assigneeList) {
    return AssigneeList.newBuilder().addAllAssignee(assigneeList).build();
  }
}
